﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mahanet.Abyss.PathCalculation
{
	public class Vector2D
	{
		#region Consts

		private static readonly double TWO_PI = 2 * Math.PI;

		#endregion

		#region C'tor

		public Vector2D(double x, double y)
		{
			this.X = x;
			this.Y = y;
		}

		public Vector2D(Coordinate2D from, Coordinate2D to)
		{
			this.X = to.X - from.X;
			this.Y = to.Y - from.Y;
		}

		public static Vector2D CreateFromAngleAndAmplitude(double amplitude, double angle)
		{
			return new Vector2D(amplitude * Math.Cos(angle), amplitude * Math.Sin(angle));
		}

		#endregion

		#region Public Properties

		public double X { get; private set; }
		public double Y { get; private set; }

		#endregion

		#region Public Methods

		public double Dot(Vector2D other)
		{
			return X * other.X + Y * other.Y;
		}

		public double Cross(Vector2D other)
		{
			return this.X * other.Y - this.Y * other.X;
		}

		public Vector2D Cross()
		{
			return new Vector2D(this.Y, -this.X);
		}

		public double LengthSquared()
		{
			return X * X + Y * Y;
		}

		public double Length()
		{
			return Math.Sqrt(LengthSquared());
		}

		public Vector2D Normalized()
		{
			if (this.IsZero())
			{
				return this.MemberwiseClone() as Vector2D;
			}

			double length = Length();
			return this / length;
		}

		public static Vector2D operator *(Vector2D vector, double scalar)
		{
			return scalar * vector;
		}

		public static Vector2D operator *(double scalar, Vector2D vector)
		{
			return new Vector2D(vector.X * scalar, vector.Y * scalar);
		}

		public static Vector2D operator /(Vector2D vector, double scalar)
		{
			return new Vector2D(vector.X / scalar, vector.Y / scalar);
		}

		public static Vector2D operator +(Vector2D a, Vector2D b)
		{
			return new Vector2D(a.X + b.X, a.Y + b.Y);
		}

		public static Coordinate2D operator +(Vector2D vector, Coordinate2D coordinate)
		{
			return coordinate + vector;
		}

		public static Coordinate2D operator +(Coordinate2D coordinate, Vector2D vector)
		{
			return new Coordinate2D(vector.X + coordinate.X, vector.Y + coordinate.Y); 
		}

		public static Coordinate2D operator -(Vector2D a, Vector2D b)
		{
			return new Coordinate2D(a.X - b.X, a.Y - b.Y);
		}

		public static Vector2D operator -(Vector2D vector)
		{
			return new Vector2D(-vector.X, -vector.Y);
		}

		public double Angle()
		{
			return Math.Atan2(this.Y, this.X);
		}

		public double AngleTo(Vector2D other)
		{
			double angle1 = this.Angle();
			double angle2 = other.Angle();
			double angleBetween = angle2 - angle1;
			if (angleBetween <= -Math.PI)
				return angleBetween + TWO_PI;
			if (angleBetween > Math.PI)
				return angleBetween - TWO_PI;
			return angleBetween;
		}

		public bool IsZero()
		{
			return PrecisionUtils.IsZero(X) && PrecisionUtils.IsZero(Y);
		}

		public Vector2D RotateBy(double angle)
		{
			return Vector2D.CreateFromAngleAndAmplitude(this.Length(), this.Angle() + angle);
		}

		#endregion
	}
}
